#pragma once

#include<iostream>
#include<unistd.h>
#include<queue>
#include<pthread.h>
using namespace std;

// const int NUM = 5;

// template<class T>
// class ThreadPool
// {
// private:
//     queue<T> _task_queue;
//     int _thread_num;
//     pthread_mutex_t _mutex;
//     pthread_cond_t _cond;

//     bool IsEmpty()
//     {
//         return _task_queue;
//     }

//     bool IsFull()
//     {
//         return _task_queue.size() == _thread_num;
//     }

//     void UnlockQueue()
//     {
//         pthread_mutex_unlock(&_mutex);
//     }

//     void WakeUp()
//     {
//         pthread_mutex_signal(&_cond);
//     }

// public:
//     ThreadPool(int num = NUM)
//     :_thread_num(num)
//     {
//         pthread_mutex_init(&_mutex);
//         pthread_cond_init(&_cond);
//     }

//     ~ThreadPool()
//     {
//         pthread_mutex_destroy($_mutex);
//         pthread_mutex_destroy(&_mutex);
//     }

//     static void* Routine(void* arg)
//     {
//         pthread_detach(pthread_self());
//         auto* self = (ThreadPool*)arg;

//         while(true)
//         {
//             self->LockQueue();
//             while(self->IsEmpty())
//             {
//                 self->Wait();
//             }

//             T task;
//             self->Pop(task);
//             self->UnlockQueue();

//             task.run();
//         }
//     }

//     void ThreadPoolInit()
//     {
//         pthread_t tid;
//         for(int i  0; i < _thead_num; i++)
//         {
//             pthread_create(&tid, nullptr, Routine, this);
//         }
//     }

//     void Push(const T& task)
//     {
//         LockQueue();
//         _task_queue.push(task);
//         UnlockQueue();
//         WakeUp();
//     }

//     void Pop(T& task)
//     {
//         task = _task_queue.front();
//         _task_queue.pop();
//     }




// };

#pragma once     
    
#include <iostream>    
#include <unistd.h>    
#include <queue>    
#include <pthread.h>    
using namespace std;    
    
const int NUM = 5;    
    
template<class T>    
class ThreadPool    
{    
  private:    
    queue<T> _task_queue;    
    int _thread_num;    
    pthread_mutex_t _mutex;    
    pthread_cond_t _cond;    
    
  private:    
    bool IsEmpty()    
    {    
      return _task_queue.size() == 0;    
    }    
    
    bool IsFull()    
    {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
      return _task_queue.size() == _thread_num;    
    }    
    
    void LockQueue()    
    {    
      pthread_mutex_lock(&_mutex);    
    }    
    
    void UnlockQueue()    
    {    
      pthread_mutex_unlock(&_mutex);    
    }    
    
    void Wait()    
    {    
      pthread_cond_wait(&_cond , &_mutex);    
    }    
    
    void WakeUp()    
    {    
      pthread_cond_signal(&_cond);    
    }    
    
  public:    
    ThreadPool(int num = NUM)    
      :_thread_num(num)    
    {    
      pthread_mutex_init(&_mutex);    
      pthread_cond_init(&_cond);    
    }    
    ~ThreadPool()    
    {    
      pthread_mutex_destroy(&_mutex);    
      pthread_mutex_destroy(&_mutex);    
    }    
    
  public:    
    static void* Routine(void* arg)    
    {    
      pthread_detach(pthread_self());    
      auto* self = (ThreadPool*)arg;    
    
      while(true)    
      {    
        self->LockQueue();    
        while(self->IsEmpty())    
        {    
          self->Wait();    
        }    
    
        T task;    
        self->Pop(task);    
        self->UnlockQueue();    
    
        task.run();    
      }    
    }    
    
    void ThreadPoolInit()    
    {    
      pthread_t tid;    
      for(int i = 0; i < _thread_num ; i++)    
      {    
        pthread_create(&tid , nullptr , Routine , this);    
      }    
    }    
    
    void Push(const T& task)    
    {    
      LockQueue();    
      _task_queue.push(task);    
      UnlockQueue();    
      WakeUp();    
    }    
    
    void Pop(T& task)    
    {    
      task = _task_queue.front();    
      _task_queue.pop();    
    }    
};    
